#include "Matrix.hpp"
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>

using namespace std;
using namespace boost;
using namespace boost::python;

void Sequencee::setSequence(const string s){
		sequence_ = s;
}

const string Sequencee::getSequence() const {
	if(sequence_.size() == 0)
		throw logic_error("No sequence available in: Sequencee::getSequence");
	
	return sequence_;
}

int const Sequencee::getSequenceSize() const {
	return sequence_.size();
}

char const Sequencee::getSequenceElement(const int i) const {
	if(i > sequence_.size())
		throw out_of_range("Sequence out of range in: Sequencee ::getSequenceElement ");

	return sequence_[i];
}

void Sequencee::setSequenceElement(const char el){
	sequence_.push_back(el);
}

Matrix::Matrix(){
}

const PMatrixType Matrix::getMatrix() const{
	if(matrix_->size() == 0)
		throw logic_error("No matrix available in: Matrix::getMatrix");

	return matrix_;
}

const PMatrixType Matrix::getPathMatrix() const {
	if(matrix_->size() == 0)
		throw logic_error("No matrix available in: Matrix::getPathMatrix");

	return pathMatrix_;
}

void Matrix::createMatrix(const PSequence a, const PSequence b, const PSimilarity_matrix s){	
	if(a == NULL || b == NULL || s == NULL)
		throw invalid_argument("Empty shared_ptr in Matrix::createMatrix");

	//zmienne pomocnicze
	int match, dismatch, d = -1;
	int s1 = a->getSequenceSize();
	int s2 = b->getSequenceSize();
	vec_int wiersz;
	vec_int wiersz2;

	//inicjalizacja macierzy
	vector < vec_int > var_mat;
	vec_int  tempVec;
	matrix_.reset();
	pathMatrix_.reset();
	matrix_ = PMatrixType(new MatrixType());
	pathMatrix_ = PMatrixType(new MatrixType());

	for(int i = 0; i <= s1; ++i){
		for(int j = 0; j <= s2; ++j){
			if (i == 0 || j == 0){
				wiersz.push_back(0);
				wiersz2.push_back(0);
			}
			else {

				if(a->getSequenceElement(i - 1) == b->getSequenceElement(j - 1)){
					match = s->getSimilarityMatrixValue(a->getSequenceElement(i - 1), b->getSequenceElement(j - 1));
					tempVec.push_back(0);
					tempVec.push_back((*matrix_)[i - 1][j - 1] + match);
					var_mat.push_back(tempVec);
					tempVec.clear();

				}
				else{
					dismatch= s->getSimilarityMatrixValue(a->getSequenceElement(i - 1), b->getSequenceElement(j - 1));
					tempVec.push_back(0);
					tempVec.push_back((*matrix_)[i - 1][j - 1] + dismatch);
					var_mat.push_back(tempVec);
					tempVec.clear();

				}

				tempVec.push_back(2);
				tempVec.push_back((*matrix_)[i - 1][j] + d);
				var_mat.push_back(tempVec);
				tempVec.clear();

				tempVec.push_back(1);
				tempVec.push_back(wiersz[j - 1] + d);
				var_mat.push_back(tempVec);
				tempVec.clear();

				tempVec.push_back(4);
				tempVec.push_back(0);

				var_mat.push_back(tempVec);
				tempVec.clear();

				int a = var_mat[0][1]; 
				int b = var_mat[0][0];

				for (int i=1; i < 4; i++){
					if(var_mat[i][1] > a){
						a = var_mat[i][1];
						b = var_mat[i][0];
					}
				}
				wiersz.push_back(a);
				wiersz2.push_back(b);
				var_mat.clear();

			}
		}
		matrix_->push_back(wiersz) ;
		pathMatrix_->push_back(wiersz2);
		wiersz2.clear();
		wiersz.clear();
	}
}

void Similarity_matrix::createSimilarityMatrix(const int *elements){
	for(int i = 0; i < 4; ++i){
		for(int j = 0; j < 4; ++j){
			similarityMatrix_[i][j] = elements[i * 4 + j];
		}
	}
}

int const Similarity_matrix::getSimilarityMatrixValue(const char a, const char b) const {
	int value;
	
	if (a=='A' && b=='A')
		value = similarityMatrix_[0][0];
	else if ((a=='A' && b=='G')||(a=='G' && b=='A'))
		value = similarityMatrix_[0][1];
	else if ((a=='A' && b=='C')||(a=='C' && b=='A'))
		value = similarityMatrix_[0][2];
	else if ((a=='A' && b=='T')||(a=='T' && b=='A'))
		value = similarityMatrix_[0][3];
	else if (a=='G' && b=='G')
		value = similarityMatrix_[1][1];
	else if ((a=='G' && b=='C')||(a=='C' && b=='G'))
		value = similarityMatrix_[1][2];
	else if ((a=='G' && b=='T')||(a=='T' && b=='G'))
		value = similarityMatrix_[1][3];
	else if (a=='C' && b=='C')
		value = similarityMatrix_[2][2];
	else if ((a=='C' && b=='T')||(a=='T' && b=='C'))
		value = similarityMatrix_[2][3];
	else if (a=='T' && b=='T')
		value = similarityMatrix_[3][3];
	else{
		throw invalid_argument("Bad sequence value in: Similarity_matrix::getSimilarityMatrixValue");
	}

	return value;
}